package com.vincent.template.config;

import com.vincent.template.commons.base.BaseResultDto;
import com.vincent.template.commons.exception.Vct520Exception;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.NestedServletException;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author Vincent Wang
 * @Date 2020/8/31 17:40
 */
@RestController
public class Vct520ErrorHandleController implements ErrorController {

    private static final String TIMESTAMP_KEY = "timestamp";
    private static final String STATUS_KEY = "status";
    private static final String ERROR_KEY = "error";
    private static final String MESSAGE_KEY = "message";
    private static final String PATH_KEY = "path";

    @Value("${error.path:/error}")
    private String errorPath;

    @Override
    public String getErrorPath() {
        return errorPath;
    }

    @RequestMapping(value = "${error.path:/error}", produces = "application/json")
    public BaseResultDto<Map<String, Object>> error(HttpServletRequest request) {
        int status = getErrorStatus(request);
        String error = HttpStatus.valueOf(status).getReasonPhrase();
        String message = getErrorMessage(request);
        String path = getRequestPath(request);
        Map<String, Object> data = new HashMap<>();
        data.put(TIMESTAMP_KEY, new Date());
        data.put(STATUS_KEY, status);
        data.put(ERROR_KEY, error);
        data.put(MESSAGE_KEY, message);
        data.put(PATH_KEY, path);
        BaseResultDto<Map<String, Object>> result = BaseResultDto.fail(message);
        result.setData(data);
        return result;
    }

    private String getRequestPath(HttpServletRequest request) {
        Object attribute = request.getAttribute("javax.servlet.error.request_uri");
        if (attribute != null) {
            return attribute.toString();
        }
        return "Unexpected error occurred";
    }

    private int getErrorStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        return statusCode != null ? statusCode : HttpStatus.INTERNAL_SERVER_ERROR.value();
    }

    private String getErrorMessage(HttpServletRequest request) {
        Object attribute = request.getAttribute("javax.servlet.error.exception");
        Object requestAttribute = request.getAttribute("org.springframework.boot.web.servlet.error.DefaultErrorAttributes.ERROR");
        if (requestAttribute instanceof Vct520Exception) {
            return ((Vct520Exception) requestAttribute).getMessage();
        }
        if (attribute instanceof NestedServletException) {
            NestedServletException exception = (NestedServletException) attribute;
            Throwable cause = exception.getCause();
            if (cause instanceof Vct520Exception) {
                return cause.getMessage();
            } else if (cause.getCause() instanceof Vct520Exception) {
                return cause.getCause().getMessage();
            }
        } else if (attribute instanceof Vct520Exception) {
            return ((Throwable) attribute).getMessage();
        } else {
            final Throwable exc = (Throwable) attribute;
            final String message = (String) request.getAttribute("javax.servlet.error.message");
            if (StringUtils.isNotBlank(message)) {
                return message;
            }
            if (exc != null) {
                return exc.getMessage();
            }
        }
        return "No message available";
    }
}
